﻿using System.Collections.Generic;
using BattleFury.Lib.Network.Packets;
using BattleFury.Lib.ServerPlayer;
using BattleFuryGameEngine.GameData;
using BattleFuryGameEngine.Time;
using System.Collections.Concurrent;
using System.ComponentModel;

namespace BattleFuryGameEngine
{
    public partial class BattleFuryGame
    {
        #region I'm a singleton
        private static BattleFuryGame _bfg;
        public static BattleFuryGame Get()
        {
            if (_bfg == null)
                _bfg = new BattleFuryGame();
            return _bfg;
        }
        #endregion

        private GenericPlayerCollection _players;
        private BackgroundWorker _mainGameLoop;

        private bool _isRunning = false;
        public bool IsRunning
        {
            get { return _isRunning; }
            set { _isRunning = value; }

        }

        private BattleFuryGame()
        {
            _players = new GenericPlayerCollection();
        }

        public bool Start()
        {
            IsRunning = true;
            _mainGameLoop = new BackgroundWorker
            {
                WorkerReportsProgress = false
            };
            _mainGameLoop.DoWork += GameMainLoop;

            _mainGameLoop.RunWorkerAsync();
            return true;
        }

        private void GameMainLoop(object sender, DoWorkEventArgs e)
        {
            double timeIncrement;
            double timeLastTime = BattleFuryTime.Now;
            while (IsRunning)
            {
                timeIncrement = BattleFuryTime.Now - timeLastTime;
                timeLastTime += timeIncrement;
                if (timeIncrement <= 0) continue;//make sure time increment is always positive

                UpdatePlayerPositions(timeIncrement);

                //TODO: update all entity position (like bullets and stuff)

                //add all new players
                AddNewPlayers();

                //remove all old players
                RemoveOldPlayers();

                //Handle Incoming Packets
                ProcessIncomingPackets();

                //TODO:  perhaps check some locking here to give users of this object time to read collections before we begin updating them?

                //Allow the cpu to breath
                System.Threading.Thread.Sleep(1);
            }
        }

        private void ProcessIncomingPackets()
        {
            IPacket p;
            while (_incomingPacketQueue.TryDequeue(out p))
            { HandlePacket(p); }
        }

        private void RemoveOldPlayers()
        {
            //remove all old players
            long tempPlayer;
            while (_removingPlayers.TryDequeue(out tempPlayer))
            { _players.RemovePlayer(tempPlayer); }
        }

        private void AddNewPlayers()
        {
            //update all entity position (like bullets and stuff)
            //add all new players
            long tempPlayer;
            while (_pendingPlayers.TryDequeue(out tempPlayer))
            {
                var gp = _players.AddPlayer(tempPlayer);
                PlayerAdded(this, new Events.PlayerAddedEventArgs(gp.Id)
                                  {
                                      Position = gp.Position,
                                      Size = gp.Size
                                  });
            }
        }

        private void UpdatePlayerPositions(double timeIncrement)
        {
            //update all player positions
            foreach (KeyValuePair<long, GenericPlayer> player in _players.PlayerCollection)
            {
                GenericPlayer p = player.Value;
                var oldPosition = p.Position;
                if (p.UpdatePosition(timeIncrement, p.Position))
                {
                    if (CollisionCheck(p, ref _players))
                    {
                        p.Position = oldPosition;
                        //We hit something, stop moving
                        p.CurrentMovement = new BattleFury.Lib.Movement();
                        PlayerMovementUpdate(this, new Events.PlayerAddedEventArgs(p.Id)
                                                   {
                                                       CurrentMovement = p.CurrentMovement,
                                                   });
                        continue;
                    }
                    PlayerPositionUpdate(this, new Events.PlayerAddedEventArgs(p.Id)
                                               {
                                                   Position = p.Position,
                                               });
                }
            }
        }

        private ConcurrentQueue<long> _pendingPlayers = new ConcurrentQueue<long>();
        public void AddPlayer(long p)
        {
            _pendingPlayers.Enqueue(p);
        }
        private ConcurrentQueue<long> _removingPlayers = new ConcurrentQueue<long>();
        public void RemovePlayer(long p)
        {
            _removingPlayers.Enqueue(p);
        }

        private static bool CollisionCheck(GenericPlayer p, ref GenericPlayerCollection Players)
        {
            foreach (var bot in Players.PlayerCollection)
            {
                if (bot.Key == p.Id) continue;
                if (bot.Value.Bounds.Intersects(p.Bounds)) return true;
            }
            return false;
        }

    }
}
